#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "autoresp.h"
#include "list.h"

extern char *errstr;
extern int errln;

int init_vardef(char *line, vardef *init)
{
    int i;
    char *tmp;
    if(!(line = strstr(line, "def")))
    {
        errstr = "<%d>ERR: Incorrect syntax, 'def' expected.";
        return 0;
    }
    line += 4;

    if(*line != '$')
    {
        errstr = "<%d>ERR: Incorrect syntax, '$' must precede vars.";
        return 0;
    }

    if((tmp = malloc(strlen(line))) == NULL)
    {
        errstr = "<%d>ERR: Ran out of memory.";
        return 0;
    }

    i = strcspn(line, " ");
    strncpy(tmp, line, i);
    init->name = malloc(strlen(tmp));
    strncpy(init->name, tmp, i);
    line += i + 1;

    init->nhash = hash(init->name);

    line += strcspn(line, "@") + 1;

    if(*line != '"')
    {
        i = strcspn(line, ";"" ""	""\n");
        strncpy(tmp, line, i);
        init->val = malloc(strlen(tmp));
        strncpy(init->val, tmp, i);
        free(tmp);
        return 1;
    }

    if(strchr(line, '"') == strrchr(line, '"'))
    {
       errstr = "<%d>ERR: Incorrect syntax, missing closing \".";
       free(tmp);
       return 0;
    }

    i = strcspn(line + 1, "\"") + 2;
    strncpy(tmp, line, i);
    init->val = malloc(strlen(tmp));
    strncpy(init->val, tmp, i);

    free(tmp);
    return 1;
}

void free_vardef(vardef *destroy)
{
    free(destroy->name);
    free(destroy->val);
}

int init_kvline(char *line, kvline *init)
{
    int i, j;
    char *tmp, *cp;

    if(!(tmp = strstr(line, "::")))
    {
        errstr = "<%d>ERR: Incorrect formatting, 'as' sequence not found.";
        return 0;
    }

    line += strspn(line, " ""	");

    for(i = 0, cp = line; *cp && *cp != ';'; i++, cp++)
        if(*cp == ':' && *(cp + 1) == ':') break;

    if(!(init->keys = malloc(i + 1)))
    {
        errstr = "<%d>ERR: Ran out of memory.";
        return 0;
    }
    strncpy(init->keys, line, i);
    j = strlen(init->keys);
    for(i = 0; i < j; i++)
        if(init->keys[i] == ':') init->keys[i] = 0;
    init->keys[i] = 0;
    tmp += 2;

    if(!(init->vals = malloc((i = strcspn(tmp, ";"" ""	""\n")))))
    {
        errstr = "<%d>ERR: Ran out of memory.";
        return 0;
    }
    strncpy(init->vals, tmp, i);
    j = strlen(init->vals);
    for(i = 0; i < j; i++)
        if(init->vals[i] == ':') init->vals[i] = 0;
    init->vals[i] = 0;

    return 1;
}

void free_kvline(kvline *destroy)
{
    free(destroy->keys);
    free(destroy->vals);
}

int init_section(char *line, vardef *vdlist, kvline *kvlist, section *init)
{
    int i;
    char *tmp;

    if(!(line = strstr(line, "SECTION")))
    {
        errstr = "<%d>ERR: Incorrect formatting, 'SECTION' expected.";
        return 0;
    }
    line += 8;

    if(*line != '@')
    {
        errstr = "<%d>ERR: Incorrect formatting, '@' not found before constant.";
        return 0;
    }
    line++;

    if((tmp = malloc(strlen(line))) == NULL)
    {
        errstr = "<%d>ERR: Ran out of memory.";
        return 0;
    }

    if(*line != '"')
    {
        i = strcspn(line, ";"" ""	""\n");
        strncpy(tmp, line, i);
        init->name = malloc(strlen(tmp));
        strncpy(init->name, tmp, i);
        free(tmp);
        return 1;
    }

    if(strchr(line, '"') == strrchr(line, '"'))
    {
       errstr = "<%d>ERR: Incorrect syntax, missing closing \".";
       free(tmp);
       return 0;
    }

    i = strcspn(line + 1, "\"") + 2;
    strncpy(tmp, line, i);
    init->name = malloc(strlen(tmp));
    strncpy(init->name, tmp, i);

    init->vdlist = vdlist;
    init->kvlist = kvlist;

    free(tmp);
    return 1;

}

void free_section(section *destroy)
{
    free(destroy->name);
}

int init_lists(FILE *dict, section *seclist)
{
    int lncnt = 1, hierarchy = 0;
    char buf[255] = {0};
    section *sectmp, *curr = seclist, *root;
    vardef *vdtmp;

    while(fgets(buf, 255, dict))
    {
        lncnt++;
        if(*buf == ';' || *buf == '\n') continue;
        printf("<%d>: %s", lncnt, buf);

        if(strstr(buf, "def"))
        {
            if(!hierarchy)
            {
                errstr = "<%d>WARN: Variable has undefined scope"
                         " and cannot be used.\n";
                errln = lncnt;
                continue;
            }

            vdtmp = malloc(sizeof(vardef));
            INIT_LIST_HEAD(&vdtmp->lp);

            if(!init_vardef(buf, vdtmp))
            {
                errln = lncnt;
                return 0;
            }

            list_add_tail(&vdtmp->lp, &curr->vdlist->lp);
        }
        else if(strstr(buf, "ENDSECTION"))
        {
            hierarchy--;
            curr = root;
        }
        else if(strstr(buf, "SECTION"))
        {
            hierarchy++;
            sectmp = malloc(sizeof(section));
            INIT_LIST_HEAD(&sectmp->lp);

            sectmp->vdlist = malloc(sizeof(vardef *));
            INIT_LIST_HEAD(&sectmp->vdlist->lp);

            sectmp->kvlist = malloc(sizeof(kvline *));
            INIT_LIST_HEAD(&sectmp->kvlist->lp);

            if(!init_section(buf, sectmp->vdlist, sectmp->kvlist, sectmp))
            {
                errln = lncnt;
                return 0;
            }

            list_add_tail(&sectmp->lp, &curr->lp);
            curr = sectmp;
        }
        else if(strstr(buf, "ENDLIBRARY"))
            hierarchy = 0;
        else if(strstr(buf, "LIBRARY"))
        {
            hierarchy = 1;
            sectmp = malloc(sizeof(section));
            INIT_LIST_HEAD(&sectmp->lp);

            sectmp->vdlist = malloc(sizeof(vardef));
            INIT_LIST_HEAD(&sectmp->vdlist->lp);

            sectmp->kvlist = malloc(sizeof(kvline));
            INIT_LIST_HEAD(&sectmp->kvlist->lp);

            if(!init_section("SECTION @\"ROOT\";", sectmp->vdlist,
                             sectmp->kvlist, sectmp))
            {
                errln = lncnt;
                return 0;
            }

            list_add_tail(&sectmp->lp, &curr->lp);
            root = (curr = sectmp);
        }
        else
        {
            if(!hierarchy)
            {
                errstr = "<%d>WARN: KV has undefined scope"
                         " and cannot be used.\n";
                errln = lncnt;
                continue;
            }

            kvline *kvtmp = malloc(sizeof(kvline));
            INIT_LIST_HEAD(&kvtmp->lp);

            if(!init_kvline(buf, kvtmp))
            {
                errln = lncnt;
                return 0;
            }

            list_add_tail(&kvtmp->lp, &curr->kvlist->lp);
        }

        *buf = 0;
    }

    return 1;
}

/*
 * String hashing function. Used for the
 * lookup/hash tables.
 */

unsigned int hash_str(void *src, int seed, int len)
{
    unsigned int m = 0x5BD1E995, r = 24, h = seed ^ len, k;

    unsigned char *data = (unsigned char *)src;

    for(k = *(unsigned int *)data; len >= 4; len -= 4, data += 4)
    {
        k *= m; k ^= k >> r; k *= m;
		h *= m; h ^= k;
    }

    switch(len)
    {
        case 3: h ^= data[2] << 16;
        case 2: h ^= data[1] << 8;
        case 1: h ^= data[0];
                h *= m;
    }

	h ^= h >> 13; h *= m; h ^= h >> 15;

	return h;
}
